/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2009 2010 2011 2012 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#include "carina/gl-window-impl.hh"
#include "carina/gl-renderer.hh"
#include "carina/common/logging.hh"

namespace Carina
{
extern string GetLastErrorString();

GLWindowImpl::GLWindowImpl(GLLibrary& lib)
    :   m_GL(lib)
{
}

void GLWindowImpl::create(WindowManager& wmgr, size_t w, size_t h, size_t samples, const WindowInfo& parent)
{
    m_MSAA = samples;
    const int pi_attr_list[] =
    {
        WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
        WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
        WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
        WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
        WGL_COLOR_BITS_ARB, 32,
        WGL_DEPTH_BITS_ARB, 24,
        WGL_STENCIL_BITS_ARB, 8,
    //    WGL_SAMPLES_ARB, samples,
        0
    };

    PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        24,
        0, 0, 0, 0, 0, 0,
        8, 0,
        0,
        0, 0, 0, 0,
        24,
        8,
        0,
        PFD_MAIN_PLANE,
        0,
        0, 0, 0,
    };


    const int ctx_attr_list[] =
    {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
        WGL_CONTEXT_MINOR_VERSION_ARB, 0,
        WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
        0
    };

    int pixel_format;
    UINT num_format;

    DWORD dwStyle;
    if(parent.getWindowId())
        dwStyle = WS_CHILD;
    else
        dwStyle = WS_OVERLAPPEDWINDOW | WS_SYSMENU;

    m_Window = CreateWindowEx(0, "CarinaWindow", "Untitled window", dwStyle,
                              0, 0, w, h, parent.getWindowId(), 0, (HINSTANCE)GetModuleHandle(nullptr), this);
    if(!m_Window)
        THROW_EXCEPTION("has failed to create window");

    m_DC = GetDC(m_Window);

    if(!m_GL.initGLWindowSystem(WindowInfo(m_Window)))
        THROW_EXCEPTION("the application has failed to initialize WGL");

    wglChoosePixelFormatARB(m_DC, pi_attr_list, nullptr, 1, &pixel_format, &num_format);
	auto ret = SetPixelFormat(m_DC, pixel_format, &pfd);
    if(!ret)
        THROW_EXCEPTION("an error has occurred while setting up the pixel format: " + GetLastErrorString());
    m_HGLRC = wglCreateContextAttribsARB(m_DC, nullptr, ctx_attr_list);
    if(!m_HGLRC)
    {
        Log::message(CE_LOG_WARNING, "has failed to initialize an OpenGL 3.0 compatible context");
        int iPixelFormat = ChoosePixelFormat(m_DC, &pfd);
        SetPixelFormat(m_DC, iPixelFormat, &pfd);
        m_HGLRC = wglCreateContext(m_DC); assert(m_HGLRC);
    }
    wglMakeCurrent(m_DC, m_HGLRC);
    if(!m_GL.initGL())
        THROW_EXCEPTION("the application has failed to initialize OpenGL");

    m_Renderer = make_aligned_shared<GLRenderer>(*this);
}

void GLWindowImpl::resize(size_t w, size_t h)
{
}

void GLWindowImpl::destroy()
{
    wglMakeCurrent(m_DC, nullptr);
    wglDeleteContext(m_HGLRC);
    ReleaseDC(m_Window, m_DC);
}

void GLWindowImpl::swapBuffers()
{
    SwapBuffers(m_DC);
}
}
